<template>
  <div style="width:100%;margin:0 auto" ref="allPage" @scroll="scrollPage">
    <canvas v-for="index in numPages" :key="index" :ref="`scrollPdfCanvas${index}`" class="pdf-preview"></canvas>
  </div>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
// eslint-disable-next-line no-restricted-imports
import { throttle } from 'lodash';
// https://github.com/mozilla/pdf.js/issues/13190
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.js';
import PDFJSWorker from 'pdfjs-dist/legacy/build/pdf.worker.entry';
// https://github.com/mozilla/pdf.js/issues/11990
pdfjsLib.GlobalWorkerOptions.workerSrc = PDFJSWorker

@Component({
  name: 'previewPdf'
})
export default class extends Vue {
  @Prop({ default: () => '' }) public url!: any;
  @Prop({ default: () => 1 }) public currentPage!: number;
  @Prop({ default: () => 0 }) public rotate!: number;
  private numPages = 0; // pdf 总页数
  private isChangeScroll = false;
  private isChangePage = false;
  private pdfData = null as any;

  // 页面滚动时计算当前在第几页
  private scrollPage = throttle((e) => {
    this.changePage(e.target);
  }, 200)

  private changePage (e) {
    // 控制currentPage触发滚动时 不走下面逻辑
    if (this.isChangePage) {
      this.isChangePage = false
      return
    }
    const pdfHeight = e.children[0].clientHeight;
    const scrollTop = e.scrollTop + e.offsetHeight;
    let page = Math.ceil(scrollTop / pdfHeight);
    if (page > this.numPages) {
      page = this.numPages;
    }
    // 当前页滚动时 不会触发currentPage变化
    if (page !== this.currentPage) {
      this.isChangeScroll = true;
    }
    this.$emit('scroll-page', page);
  }

  private loadPdfData () {
    this.$emit('load-success', true);
    // 用于异步获取PDf文档，发送多个Ajax请求以块的形式下载文档。它返回一个Promise，该Promise的成功回调传递一个对象，该对象包含PDF文档的信息，该回调中的代码将在完成PDf文档获取时执行。
    this.pdfData = pdfjsLib.getDocument(this.url);
    this.renderScrollPdf();
  }

  private renderScrollPdf () {
    this.pdfData.promise.then((pdf) => {
      this.numPages = pdf.numPages;
      this.$emit('all-pages', this.numPages);
      this.renderScrollPdfPage(1);
      this.$nextTick(() => {
        this.$emit('load-success', false);
      })
    })
  }

  private renderScrollPdfPage (num, rotate = 0) {
    this.pdfData.promise.then((pdf) => {
      const numPages = pdf.numPages;
      // 用于获取PDF文档中的各个页面
      pdf.getPage(num)
        .then((page) => {
          const canvas = this.$refs[`scrollPdfCanvas${num}`][0];
          const scale = 2; // 解决字体模糊问题

          // 针对提供的展示比例，返回PDf文档的页面尺寸 getViewport({ scale, rotation, dontFlip })
          const viewport = page.getViewport({ scale, rotate });
          const { width, height } = viewport
          canvas.height = height;
          canvas.width = width;

          const ctx = canvas.getContext('2d');
          const renderContext = {
            canvasContext: ctx,
            viewport
          };
          // 渲染PDF
          page.render(renderContext).promise.then(() => {
            if (num < numPages) {
              this.renderScrollPdfPage(num + 1, rotate)
            }
          });
        })
    })
  }

  @Watch('currentPage')
  private currentPageChanged (val: number, oldVal: number) {
    // 控制通过鼠标滚动触发时 不走下面逻辑
    if (this.isChangeScroll) {
      this.isChangeScroll = false
      return
    }
    // currentPage变化时 重置滚动条
    this.isChangePage = true
    const allPage = this.$refs.allPage as any;
    const pdfHeight = allPage.children[0].clientHeight;
    allPage.scrollTop = pdfHeight * (val - 1);
  }

  @Watch('url')
  private urlChanged () {
    this.loadPdfData();
  }

  @Watch('rotate')
  private rotateChanged (val: number) {
    this.renderScrollPdfPage(1, val)
  }

  private mounted () {
    this.loadPdfData();
  }
}
</script>
<style lang="stylus" scoped>
.pdf-preview {
  padding 8px
  width 100%
}
</style>
